<?php

/**
 * DoctrineTemplateListenerSluggableExt
 * Easily create a slug for each record based on a specified set of fields
 *
 * @package     flexiwik
 * @subpackage  template.listener
 */
class DoctrineTemplateListenerSluggableExt extends Doctrine_Template_Listener_Sluggable
{
  public function preUpdate(Doctrine_Event $event)
  {
    if (false !== $this->_options['unique'])
    {
      $record = $event->getInvoker();
      $name = $record->getTable()->getFieldName($this->_options['name']);
      
      $modified_fields = false;
      
      foreach ($this->_options['fields'] as $field)
      {
        $modified_fields = $record->isColumnModified($field);
      }
      
      if (
           $modified_fields ||
           (false !== $this->_options['canUpdate'] && array_key_exists($name, $record->getModified()))
         )
      {
        $record->$name = '';
        $record->$name = $this->buildSlug($record);
      }
    }
  }

  /**
   * Creates a unique slug for a given Doctrine_Record. This function enforces the uniqueness by 
   * incrementing the values with a postfix if the slug is not unique
   *
   * @param Doctrine_Record $record 
   * @return string $slug
   */
  public function getUniqueSlug($record)
  {
    $name           = $record->getTable()->getFieldName($this->_options['name']);
    $slugFromFields = '';
    foreach ($this->_options['fields'] as $field)
    {
      $slugFromFields .= ($record->$field) ? $record->$field . ' ' : '';
    }
    $slugFromFields = trim($slugFromFields);
    
    $proposal = $record->$name ? $record->$name : $slugFromFields ? $slugFromFields : 'untitled';
    $proposal = call_user_func_array($this->_options['builder'], array($proposal, $record));
    $slug     = $proposal;

    $whereString = 'r.' . $name . ' LIKE ?';
    $whereParams = array($proposal.'%');
        
    if ($record->exists())
    {
      $identifier = $record->identifier();
      $whereString .= ' AND r.' . implode(' != ? AND r.', $record->getTable()->getIdentifierColumnNames()) . ' != ?';
      $whereParams = array_merge($whereParams, array_values($identifier));
    }

    foreach ($this->_options['uniqueBy'] as $uniqueBy)
    {
      if (is_null($record->$uniqueBy))
      {
        $whereString .= ' AND r.'.$uniqueBy.' IS NULL';
      }
      else
      {
        $whereString .= ' AND r.'.$uniqueBy.' = ?';
        $whereParams[] =  $record->$uniqueBy;
      }
    }

    // Disable indexby to ensure we get all records
    $originalIndexBy = $record->getTable()->getBoundQueryPart('indexBy');
    $record->getTable()->bindQueryPart('indexBy', null);

    $query = kcDoctrineQuery::create()
             ->select('r.'.$name)
             ->from(get_class($record).' r')
             ->where($whereString , $whereParams)
             ->setHydrationMode(Doctrine::HYDRATE_ARRAY);

    // We need to introspect SoftDelete to check if we are not disabling unique records too
    if ($record->getTable()->hasTemplate('Doctrine_Template_SoftDelete'))
    {
	  $softDelete = $record->getTable()->getTemplate('Doctrine_Template_SoftDelete');
	
	  // we have to consider both situations here
      $query->addWhere('(r.' . $softDelete->getOption('name') . ' = true OR r.' . $softDelete->getOption('name') . ' IS NOT NULL OR r.' . $softDelete->getOption('name') . ' = false OR r.' . $softDelete->getOption('name') . ' IS NULL)');
    }

    $similarSlugResult = $query->execute();

    // Change indexby back
    $record->getTable()->bindQueryPart('indexBy', $originalIndexBy);

    $similarSlugs = array();
    foreach ($similarSlugResult as $key => $value)
    {
      $similarSlugs[$key] = $value[$name];
    }

    $i = 1;
    while (in_array($slug, $similarSlugs))
    {
      $slug = call_user_func_array($this->_options['builder'], array($proposal.'-'.$i, $record)); 
      $i++;
    }
    
    return  $slug;
  }
}
